Skip to content

Conversation

@georgeweiler
Copy link
Contributor

@georgeweiler georgeweiler commented Feb 3, 2026

Explanation

Refactors region-dependent reset behavior into a single helper and improves abort handling so dependent resources stay in sync.

Changes

  • Introduce DEPENDENT_RESOURCE_KEYS and resetResource() so "reset one resource" is defined once and driven by getDefaultRampsControllerState().
  • Refactor resetDependentResources() to call getDefaultRampsControllerState() once and loop over the keys instead of many manual assignments.
  • Use resetResource(state, 'paymentMethods') in setSelectedProvider and setSelectedToken so payment methods are fully reset (including isLoading and error), fixing stale loading/error state when changing or clearing provider/token.
  • Type reset helpers to accept Draft<RampsControllerState> so they can be used inside update() without casts and avoid TS2589.
  • Update abort logic for dependent resources (abort handling aligned with the new reset flow).

Result

Single source of truth for default/reset state, less duplicated code, and correct clearing of paymentMethods (and optionally quotes.selected) on region or selection changes.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Touches RampsController request lifecycle (caching/loading/error updates and abort races) and reset behavior for multiple region-dependent resources, which can affect UI state consistency under concurrency. Changes are well-covered by additional defensive tests but still impact core controller behavior.

Overview
Refactors region-dependent state resets to a shared resetResource helper driven by getDefaultRampsControllerState(), and uses it to fully reset paymentMethods (including isLoading/error) when clearing or changing selected provider/token.

Hardens request/resource bookkeeping: PendingRequest now tracks resourceType, resource field updates become defensive/immutable, and getCountries() defensively stores [] when the service returns a non-array.

Updates tests and changelog to cover the new reset behavior, hydration/refetch expectations, and timing/race-condition scenarios around dependent resources and loading/error state.

Written by Cursor Bugbot for commit 0a1775b. This will update automatically on new commits. Configure here.

@georgeweiler
Copy link
Contributor Author

cursor review

@georgeweiler georgeweiler changed the title refactor: clean up request logic and state clearning mechanism refactor: clean up request logic and state clearing mechanism Feb 3, 2026
Base automatically changed from ramps-loading-state to main February 3, 2026 12:58
@georgeweiler georgeweiler marked this pull request as ready for review February 3, 2026 13:13
@georgeweiler georgeweiler requested review from a team as code owners February 3, 2026 13:13
@georgeweiler georgeweiler changed the title refactor: clean up request logic and state clearing mechanism refactor(ramps): streamline RampsController reset and abort logic Feb 3, 2026
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

if (resource) {
(resource as Record<string, unknown>)[field] = value;
}
(state as Record<string, unknown>)[resourceType] = next;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Race condition in resource field updates can lose data

High Severity

The refactored #updateResourceField method reads this.state[resourceType] outside the update() callback, creates a new object with the updated field, then replaces the entire resource inside the callback. This creates a race condition: if another update() modifies any property of the resource (like data or selected) between capturing the snapshot and executing the callback, those changes are overwritten by the stale next object. The old implementation correctly mutated the draft state inside the callback, which is the proper Immer pattern.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant